#include "component.h"
#include "device.h"

#define EXTEND_OTA_LOG(format, ...) OSAL_LOG(C_CYAN "[Extend_kds_OTA.c] " format C_NONE, ##__VA_ARGS__)

#define EVENT_EXTEND_KDS        (0x00000001)
static VirtualHardware_enum_t v_uart = vUART_4; //串口设备
static RingBufferHandle_t rx_handle = NULL;     //接收缓存句柄

#pragma pack(1)
/* 协议头结构 */
typedef struct
{
	uint8_t stx;  //数据头
	uint16_t sum; //校验和
	uint8_t len;  //数据长度
}ProtocolDataHead_t;

/* 命令包结构 */
typedef struct
{
	ProtocolDataHead_t head;
	uint8_t cmd;
	uint8_t dataBuf[];//数据域长度不定
}CmdPacket_stu_t;

/* 确认包结构 */
typedef struct
{
	ProtocolDataHead_t head;
	uint8_t confrim;
}ConfrimPacket_stu_t;

/* 应答包结构 */
typedef struct
{
	ProtocolDataHead_t head;
	uint8_t cmd;
	uint8_t ret;
	uint8_t dataBuf[];//数据域长度不定
}AckPacket_stu_t;
#pragma pack()

///////////// 定义协议解析层数据结构 /////////////////////////////////////////////////////////////////////////////////////////
#define PROTOCOL_CMD_PACKET_STX      0XF5   //命令包数据头
#define PROTOCOL_CONFRIM_PACKET_STX  0X5F   //确认包数据头
#define PROTOCOL_ACK_PACKET_STX      0X5F   //应答包数据头

#define PACK_TYPE_CONFRIM            0X00   //应答包类型：确认包
#define PACK_TYPE_ACK                0X01   //应答包类型：应答包

#define PROTOCOL_SEND_TIMES          3      //锁发送命令包的最大次数
#define PROTOCOL_RESEND_CMD_TIMEOUT  200    //锁重发命令包间隔时间ms
#define PROTOCOL_WAIT_ACK_TIMEOUT    3000   //锁发出命令包后，等待应答包的时间ms
#define PROTOCOL_SEND_ACK_TIME_GAP   20     //锁发出多包ACK的间隔时间ms


#define EXTEDN_KDSOTA_RX_BUFF_LEN  (1100)  
#define UART_PRO_PACKET_LEN(n)       ( (n == 0) ? (1035 + 6) : (n) )//特殊处理：长度为0表示1035+6



///////////// 定义协议应用层数据结构 /////////////////////////////////////////////////////////////////////////////////////////
#define DFU_CMD_START          0XA0 //启动升级
#define DFU_CMD_GET_INFO       0X90 //获取固件信息
#define DFU_CMD_REPORT_STATE   0X91 //上报升级状态
#define DFU_CMD_GET_DATA       0X92 //获取固件数据包

static uint32_t firmwarePacketNum;

#pragma pack(1)
/* 命令包发送状态信息 */
static struct
{
	uint8_t  dataBuf[64];             //发送缓存（暂存重发包的内容）
	uint8_t  dataLen;                 //数据长度

	uint8_t  cmd;                     //发送的命令
	uint8_t  cnt;                     //发送次数
	uint32_t timeStamp;               //最后一次发送的时间戳
    uint32_t waitAckTimeout;          //等待应答超时时间
	void (*cb)(uint8_t*, uint16_t);   //cb有效：等待ACK包， cb为空：不等待ACK包

	enum
	{                                 //当前发送的状态
		TX_STA_IDLE,                  //空闲
		TX_STA_WAIT_CONFRIM,          //等待模块回复确认包
		TX_STA_WAIT_ACK,              //等待模块回复应答包
	}status;
}CmdPacketTxInfo;

/* 命令包接收状态信息 */
static struct
{
    uint8_t cmd;                        //收到的命令
    uint8_t payload[64];                //有效数据域
    uint8_t len;                        //长度
}CmdPacketRxInfo;
#pragma pack()

static uint8_t protocolBuf[1100];//协议解析缓存
static uint16_t protocolCount;              //协议解析计数变量

static void Extend_KdsOta_GetFirmwareDataCb(uint8_t *pData, uint16_t len);


/**
  * @brief  发送数据给应用层
  * @param  psn：文件分包编号
  *         psn为FFFD：表示数据域为文件名和文件长度（ASCII）
  *         psn为FFFF：表示文件传输结束（正常结束）
  *         psn为FFFE：表示文件传输结束（ymodem出错）
  *         其他值：正常的数据包编号
  * 
  * @param  data：数据内容
  * @param  size：数据包长度
  */
static void Extend_KdsOta_PublishToApp(uint16_t psn, uint8_t *data, uint16_t size)
{
    Extend_KdsOta_Msg_stu_t *Extend_KdsOta_msg = NULL;
    
    Extend_KdsOta_msg = (Extend_KdsOta_Msg_stu_t *)OSAL_Malloc(sizeof(Extend_KdsOta_Msg_stu_t) + size);
    if (Extend_KdsOta_msg != NULL)
    {
        Extend_KdsOta_msg->psn = psn;
        Extend_KdsOta_msg->size = size;
        memcpy(Extend_KdsOta_msg->data, data, size);
        OSAL_MessagePublish(Extend_KdsOta_msg, sizeof(Extend_KdsOta_Msg_stu_t) + size);
        OSAL_Free(Extend_KdsOta_msg);
    }
}

/**
  * @brief  计算校验和
  * @note
  *
  * @param  pData：数据指针
  * @param  len：数据长度
  * @return 成功返回校验和
  */
static uint16_t Extend_KdsOta_CalcCheckSum(uint8_t *pData, uint16_t len)
{
	uint32_t sum = 0;

	for (; len; len--)
	{
		sum += *pData++;
	}
	return (uint16_t)(sum & 0XFFFF);
}


/**
  * @brief  串口中断接收回调函数
  * @note        
  * @param  data:收到的数据
  * @param  len:数据长度
  */
static void Extend_KdsOta_ReceiveCb(VirtualHardware_enum_t dev, void *data, uint32_t len)
{
    uint8_t *tmp = (uint8_t *)data;

    if (rx_handle == NULL)
    {
        return;
    }

    for (uint32_t i = 0; i < len; i++)
    {
        if (OSAL_RingBufferWrite(rx_handle, tmp + i) != SUCCESS)
        {
            return;
        }
    }
}

/**
  * @brief  命令发送回调（发出命令后，无线模块回复的结果）
  * @note
  *
  * @param  type：包类型
  *         PACK_TYPE_CONFRIM：确认包
  *         PACK_TYPE_ACK：应答包
  * @param  cmd：命令
  * @param  pRes：命令发送处理结果
  */
static void Extend_KdsOta_SendCmdPacketCallback(uint8_t type, uint8_t cmd, uint8_t *ackData, uint16_t len)
{
    /* 命令执行结果，回调通知主任务 */
    if (cmd == CmdPacketTxInfo.cmd)
    {
        if (type == PACK_TYPE_CONFRIM)
        {
            EXTEND_OTA_LOG("Received confirm packet [%#X]\r\n", *ackData);
        }
        else if (type == PACK_TYPE_ACK)
        {
            EXTEND_OTA_LOG("Received ack packet [cmd:%#X, %#X]\r\n", cmd, *ackData);
            if (CmdPacketTxInfo.cb != NULL)
            {
                CmdPacketTxInfo.cb(ackData, len);
            }
        }
    }
}

/**
  * @brief  Extend_KdsOta初始化
  *
  * @note   注册RX回调、创建环形接收缓存
  */
static void Extend_KdsOta_Init(void)
{
    if (rx_handle == NULL)
    {
        rx_handle = OSAL_RingBufferCreate(EXTEDN_KDSOTA_RX_BUFF_LEN, 1);
        Device_RegisteredCB(v_uart, Extend_KdsOta_ReceiveCb);
    }
}

/**
  * @brief  协议层初始化
  *
  * @note
  */
void Extend_KdsOta_ProtocolInit(void)
{
	CmdPacketTxInfo.status = TX_STA_IDLE;

	memset(protocolBuf, 0, sizeof(protocolBuf));
	protocolCount = 0;
    OSAL_RingBufferReset(rx_handle);
}


/**
  * @brief  命令包发送接口
  * @note   打包需要发出的数据，填入发送缓存区
  *
  * @param  cmd：发出的命令
  * @param  pData：数据指针
  * @param  dataLen：数据长度
  * @param  waitAckEnable：1使能等待应答包，0不用处理应答包
  */
ErrorStatus Extend_KdsOta_SendCmdPacket(uint8_t cmd, uint8_t *pData, uint8_t dataLen, void (*callback)(uint8_t*, uint16_t))
{
	CmdPacket_stu_t *pCmdPacket = (CmdPacket_stu_t *)CmdPacketTxInfo.dataBuf;

	if (CmdPacketTxInfo.status != TX_STA_IDLE)
	{
        EXTEND_OTA_LOG("SendCmdPacket error.\r\n");
		return ERROR;
	}

	pCmdPacket->head.stx = PROTOCOL_CMD_PACKET_STX;
	pCmdPacket->head.len = dataLen + 1;//数据域长度+cmd
	pCmdPacket->cmd = cmd;
	memcpy(pCmdPacket->dataBuf, pData, dataLen);
	pCmdPacket->head.sum = Extend_KdsOta_CalcCheckSum(&(pCmdPacket->cmd), dataLen + 1);
	CmdPacketTxInfo.dataLen = sizeof(CmdPacket_stu_t) + dataLen;//整包数据长度

	CmdPacketTxInfo.cmd = cmd;
	CmdPacketTxInfo.cb = callback;

	CmdPacketTxInfo.cnt = 0;
	CmdPacketTxInfo.status = TX_STA_WAIT_CONFRIM;
	return SUCCESS;
}


//处理固件数据包
//pData指向cyacd文件的行首，每个包长度为一行
static void Extend_KdsOta_ProcessFirmwareData( uint8_t ret,uint8_t *pData,uint16_t len)
{
	EXTEND_OTA_LOG("len = %d...%d",len,firmwarePacketNum);
    Extend_KdsOta_PublishToApp(firmwarePacketNum,pData,len);
    if (ret == 0X82)//最后一包
    {	
        EXTEND_OTA_LOG("publishtoapp 0XFFFF");
        Extend_KdsOta_PublishToApp(0xFFFF,NULL, 0);
    }
    else
    {
        /* 继续获取固件数据包 */
        firmwarePacketNum++;
        Extend_KdsOta_SendCmdPacket(DFU_CMD_GET_DATA, (uint8_t*)&firmwarePacketNum, sizeof(firmwarePacketNum), Extend_KdsOta_GetFirmwareDataCb);
    }
}

//获取固件数据包回调...第一包是固件productID信息
static void Extend_KdsOta_GetFirmwareDataCb(uint8_t *pData, uint16_t len)
{
    uint32_t packetNum = pData[1] | (pData[2] << 8) | (pData[3] << 16) | (pData[4] << 24);

    EXTEND_OTA_LOG("Extend_KdsOta_GetFirmwareDataCb,ret:%#X, number:%#X, len=%d \r\n", pData[0], packetNum,len);
    if (packetNum != firmwarePacketNum)
    {
        Extend_KdsOta_PublishToApp(0xFFFE,NULL, 0);
        EXTEND_OTA_LOG("packet number error\r\n");
        return;
    }

    if (pData[0] == 0X80 || pData[0] == 0X82)
    {
        Extend_KdsOta_ProcessFirmwareData( pData[0],&pData[5],len-5);
    }
}

//获取固件信息回调
static void Extend_KdsOta_GetFirmwareInfoCb(uint8_t *pData, uint16_t len)
{
    EXTEND_OTA_LOG("Extend_KdsOta_GetFirmwareInfoCb,ret:%#X\r\n", pData[0]);
    if (pData[0] == 0X80)
    {
        uint8_t temp[30]="this_is_extend_kds_ota 1234";
        Extend_KdsOta_PublishToApp(0xFFFD,temp, 30);

        /* 获取固件信息成功，紧接着获取第一包固件数据 */
        firmwarePacketNum = 1;
        Extend_KdsOta_SendCmdPacket(DFU_CMD_GET_DATA, (uint8_t*)&firmwarePacketNum, sizeof(firmwarePacketNum), Extend_KdsOta_GetFirmwareDataCb);
    }
}

//处理收到的命令
static ErrorStatus Extend_KdsOta_ProcessRxCmd(uint8_t cmd, uint8_t *pData, uint8_t *pLen)
{
    EXTEND_OTA_LOG("process cmd:%#X\r\n", cmd);
    if (cmd == DFU_CMD_START)
    {
        /* 填充回复的ACK内容 */
        *pLen = 0;

        if (pData[0] == 0X3A) //启动升级
        {
            /* 发出获取固件信息命令 */
            Extend_KdsOta_SendCmdPacket(DFU_CMD_GET_INFO, NULL , 0, Extend_KdsOta_GetFirmwareInfoCb);
        }
        return SUCCESS;
    }
    *pLen = 0;
    return ERROR;
}

/**
  * @brief  从底层环形缓存获取一包数据并解析
  *
  * @note   底层驱动收到数据，会暂存在环形缓存区
  *
  * @return 返回解析后的数据包地址，解析失败返回NULL
  */
static uint8_t *Extend_KdsOta_GetOnePacket(void)
{
	uint16_t sum = 0, packetLen = 0, tmpLen = 0;

    tmpLen = OSAL_RingBufferGetValidSize(rx_handle);
    
	for (; tmpLen; tmpLen--)
	{
        
        uint8_t tmpData = 0;
        OSAL_RingBufferRead(rx_handle, &tmpData);
		if (protocolCount == 0)
		{
			if (tmpData == PROTOCOL_CMD_PACKET_STX          //命令帧
			 || tmpData == PROTOCOL_CONFRIM_PACKET_STX      //确认帧
			 || tmpData == PROTOCOL_ACK_PACKET_STX)         //应答帧
			{
				protocolBuf[0] = tmpData;
				protocolCount = 1;
			}

		}
		else if (protocolCount > 0)     //包头通过后,才开始解析数据
		{
			protocolBuf[protocolCount] = tmpData;
			protocolCount = protocolCount + 1;
			if (protocolCount > 4)
			{
				ProtocolDataHead_t *pPacketHead = (ProtocolDataHead_t *)protocolBuf;
				packetLen = UART_PRO_PACKET_LEN(pPacketHead->len) + 4;
				if (protocolCount >= 1100 || packetLen == protocolCount)
				{
					if (packetLen == protocolCount)
					{
						sum = Extend_KdsOta_CalcCheckSum(protocolBuf + 4, UART_PRO_PACKET_LEN(pPacketHead->len));
						if (pPacketHead->sum == sum)
						{
							protocolCount = 0;
							return protocolBuf;//接收完成
						}
						else
						{
							EXTEND_OTA_LOG("CRC ERR, calc_crc:%04X  packet_crc:%04X\r\n", sum, pPacketHead->sum);
							for (int i=0; i<packetLen; i++)
							{
							 	EXTEND_OTA_LOG("%02X ", protocolBuf[i]);	
							}
							EXTEND_OTA_LOG("\r\n");
						}
					}
					protocolCount = 0;
					return NULL;
				}
			}
		}
	}
	return NULL;
}


/**
  * @brief  发出确认包
  *
  * @note   MCU收到无线模组发过来命令后，通过调用该函数给模组回复确认包
  */
static void Extend_KdsOta_SendConfrimPacket(uint8_t ack)
{
	ConfrimPacket_stu_t packet;

	packet.head.stx = PROTOCOL_CONFRIM_PACKET_STX;
	packet.head.len = 1;
	packet.confrim = ack;
	packet.head.sum = Extend_KdsOta_CalcCheckSum((uint8_t*)&(packet.confrim), packet.head.len);

    EXTEND_OTA_LOG("Send confrim packet, ack: %#X\r\n", ack);
    Device_Write(v_uart, (uint8_t*)&packet, sizeof(ConfrimPacket_stu_t), 0);
}

/**
  * @brief  处理接收的命令包（暂存命令包，立即回复确认包）
  * @note
  *
  * @param  pCmdPacket：数据包指针
  */
static void Extend_KdsOta_ProcessRxCmdPacket(CmdPacket_stu_t *pCmdPacket)
{
	/* 将接收的命令写入CmdRxBuffer */
    if (CmdPacketRxInfo.cmd == 0)
    {
        CmdPacketRxInfo.cmd = pCmdPacket->cmd;
        CmdPacketRxInfo.len = pCmdPacket->head.len - 1;
        memcpy(CmdPacketRxInfo.payload, pCmdPacket->dataBuf, pCmdPacket->head.len - 1);
        EXTEND_OTA_LOG("Recv cmd packet: %#X\r\n", pCmdPacket->cmd);

		/* 发出确认包 */
		Extend_KdsOta_SendConfrimPacket(0X80);
    }
    else
    {
        EXTEND_OTA_LOG("Recv cmd packet: %#X, rxbuffer overflow\r\n", pCmdPacket->cmd);

		/* 发出确认包 */
		Extend_KdsOta_SendConfrimPacket(0X81);
    }
}

/**
  * @brief  处理接收的确认包
  * @note
  *
  * @param  pConfrimPacket：确认包数据指针
  */
static void Extend_KdsOta_ProcessRxConfrimPacket(ConfrimPacket_stu_t *pConfrimPacket)
{
	if (CmdPacketTxInfo.status == TX_STA_WAIT_CONFRIM)
	{
        Extend_KdsOta_SendCmdPacketCallback(PACK_TYPE_CONFRIM, CmdPacketTxInfo.cmd, &(pConfrimPacket->confrim), 1);
		if (CmdPacketTxInfo.cb != NULL)//需要等待 应答包
		{
			CmdPacketTxInfo.status = TX_STA_WAIT_ACK;
			CmdPacketTxInfo.timeStamp = OSAL_GetTickCount();
		}
		else
		{
			CmdPacketTxInfo.status = TX_STA_IDLE;
		}
	}
}

/**
  * @brief  处理接收的应答包
  * @note
  *
  * @param  pAckPacket：应答包数据指针
  */
static void Extend_KdsOta_ProcessRxAckPacket(AckPacket_stu_t *pAckPacket)
{
	if (CmdPacketTxInfo.status == TX_STA_WAIT_ACK)
	{
        uint16_t len = UART_PRO_PACKET_LEN(pAckPacket->head.len) - 1;

		CmdPacketTxInfo.status = TX_STA_IDLE;
        Extend_KdsOta_SendCmdPacketCallback(PACK_TYPE_ACK, pAckPacket->cmd, &(pAckPacket->ret), len);
	}
}

/**
  * @brief  解析接收的数据包
  * @note
  *
  * @param  pData：数据包地址
  */
static int Extend_KdsOta_ParseRxPacket(uint8_t *pData)
{
	ProtocolDataHead_t *pPacketHead = (ProtocolDataHead_t *)pData;
	if (pPacketHead->stx == PROTOCOL_CMD_PACKET_STX)
	{
		/* 处理接收的命令包 */
		Extend_KdsOta_ProcessRxCmdPacket((CmdPacket_stu_t *)pData);
	}
	else if (pPacketHead->len == 1)
	{
		/* 处理接收的确认包 */
		Extend_KdsOta_ProcessRxConfrimPacket((ConfrimPacket_stu_t *)pData);
	}
	else
	{
		/* 处理接收应答包 */
		Extend_KdsOta_ProcessRxAckPacket((AckPacket_stu_t *)pData);
		return 1;
	}
	return 0;
}

/**
  * @brief  重发命令包，并处理超时逻辑
  *
  * @note   MCU发出命令包后，超时没有收到确认包，就重发
  */
static void Extend_KdsOta_ResendCmdPacket(void)
{
	uint32_t currentTime = OSAL_GetTickCount();

	if (CmdPacketTxInfo.status == TX_STA_WAIT_CONFRIM)//等待确认包
	{
		if (OSAL_PastTime(currentTime, CmdPacketTxInfo.timeStamp)  > PROTOCOL_RESEND_CMD_TIMEOUT || CmdPacketTxInfo.cnt == 0)
		{
			if (CmdPacketTxInfo.cnt >= PROTOCOL_SEND_TIMES)
			{
				//ERR:等待确认包超时
                EXTEND_OTA_LOG("Wait confirm packet timeout.\r\n");
				CmdPacketTxInfo.status = TX_STA_IDLE;
            }
			else
			{
                Device_Write(v_uart, CmdPacketTxInfo.dataBuf, CmdPacketTxInfo.dataLen, 0);
				CmdPacketTxInfo.timeStamp = currentTime;
				CmdPacketTxInfo.cnt++;
			}
		}
	}
	else if (CmdPacketTxInfo.status == TX_STA_WAIT_ACK)//等待应答包
	{
        if (OSAL_PastTime(currentTime, CmdPacketTxInfo.timeStamp) > PROTOCOL_WAIT_ACK_TIMEOUT)
		{
			//ERR:等待应答包超时
			EXTEND_OTA_LOG("Wait ack packet timeout.\r\n");
			CmdPacketTxInfo.status = TX_STA_IDLE;
		}
	}
}
/**
  * @brief  发出应答包
  * @note   MCU收到无线模组发过来命令后，通过调用该函数给模组回复应答包
  *
  * @param  cmd：命令
  * @param  res：命令处理结果
  * @param  pAckData：应答数据指针
  * @param  ackDataLen：数据长度
  */
static void Extend_KdsOta_SendAckPacket(uint8_t cmd, ErrorStatus res, uint8_t *pAckData, uint8_t ackDataLen)
{
	uint8_t buffer[MAX_PACKAGE_LEN]={0};
	AckPacket_stu_t *pAckPacket = (AckPacket_stu_t *)buffer;

	pAckPacket->head.stx = PROTOCOL_ACK_PACKET_STX;
	pAckPacket->head.len = ackDataLen + 2;
	pAckPacket->cmd = cmd;
	pAckPacket->ret = (res == SUCCESS) ? 0X80 : 0X81;
	memcpy(pAckPacket->dataBuf, pAckData, ackDataLen);
	pAckPacket->head.sum = Extend_KdsOta_CalcCheckSum((uint8_t*)&(pAckPacket->cmd), pAckPacket->head.len);
	Device_Write(v_uart, buffer, sizeof(AckPacket_stu_t) + ackDataLen, 0);
}

/**
  * @brief  处理接收的命令包（执行命令包，回复执行结果ACK包）
  *
  * @note   有些命令需要MCU发出多个应答包
  */
static void Extend_KdsOta_ProcessSendAckPacket(void)
{
    ErrorStatus res = ERROR;

    if (CmdPacketRxInfo.cmd == 0)
    {
        return;
    }
    res = Extend_KdsOta_ProcessRxCmd(CmdPacketRxInfo.cmd, CmdPacketRxInfo.payload, &CmdPacketRxInfo.len);
    /* 发出ACK包 */
    Extend_KdsOta_SendAckPacket(CmdPacketRxInfo.cmd, res, CmdPacketRxInfo.payload, CmdPacketRxInfo.len);
    CmdPacketRxInfo.cmd = 0;
}

/**
  * @brief  通信协议事件处理
  *
  * @note   该函数必须循环调用
  */
int Extend_KdsOta_Continue(void)
{
	uint8_t *pData = Extend_KdsOta_GetOnePacket();
	uint8_t res = 0;

    if (pData != NULL)
    {
        res = Extend_KdsOta_ParseRxPacket(pData);  //解析收到的包
    }
    Extend_KdsOta_ResendCmdPacket();               //处理命令包重发逻辑
    Extend_KdsOta_ProcessSendAckPacket();          //处理接收的命令包（执行命令包，回复执行结果ACK包）
	return res;
}

/**
  * @brief  Extend_KdsOta任务函数
  *
  * @note   1.任务函数内不能写阻塞代码
  *         2.任务函数每次运行只处理一个事件
  *         
  * @param  event：当前任务的所有事件
  *
  * @return 返回未处理的事件
  */
static uint32_t Extend_KdsOta_Task(uint32_t event)
{
	if (event & EVENT_SYS_START)
	{
        EXTEND_OTA_LOG("Extend_KdsOta Task start\r\n");
        Extend_KdsOta_Init();
        Extend_KdsOta_ProtocolInit();
        OSAL_EventRepeatCreate(COMP_EXTEND_KDS_OTA, EVENT_EXTEND_KDS, 10, EVT_PRIORITY_MEDIUM);
        return ( event ^ EVENT_SYS_START );
    }

    if (event & EVENT_EXTEND_KDS)
    {
        Extend_KdsOta_Continue();
        return ( event ^ EVENT_EXTEND_KDS );
    }

    return 0;
}
COMPONENT_TASK_EXPORT(COMP_EXTEND_KDS_OTA, Extend_KdsOta_Task, 0);
